﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using SimpleData.ObjectModel;

namespace SimpleData.Storage.Common
{

	/// <summary>
	/// 
	/// </summary>
	internal class UpdateCommandBuilder
	{
		private const string SqlUpdateCommandPart1 = @"MERGE INTO dataObject_values as tgt USING (";
		private const string SqlUpdateCommandPart2 =
	@") AS src ON (tgt.ColumnKey=src.clm) AND tgt.ColumnKey LIKE @Key
WHEN MATCHED THEN UPDATE SET Value=src.value
WHEN NOT MATCHED THEN INSERT (ColumnKey,Value) VALUES(src.clm,src.value)
WHEN NOT MATCHED BY SOURCE THEN	DELETE;";

		private int parameterCount = 0;
		private StringBuilder sql;
		private List<SqlParameter> parameters;
		private DataObjectColumnKey key;

		public UpdateCommandBuilder()
		{
			this.sql = new StringBuilder( 1024 * 3 );
			this.parameters = new List<SqlParameter>( 30 );
		}

		public bool Started
		{
			get { return this.parameterCount > 0; }
		}


		public void IncludeColumnKey( DataObjectColumnKey key )
		{
			this.key = key;
		}

		public void IncludeColumnValues( IEnumerable<DataObjectColumn> columns )
		{
			foreach ( var column in columns )
				this.IncludeColumnValue( column );
		}

		public void IncludeColumnValue( DataObjectColumn column )
		{
			this.IncludeColumnValue( column.Name, column.Value );
		}

		public void IncludeColumnValue( FieldName columnName, byte[] columnValue )
		{
			if ( columnValue == null || columnValue.Length == 0 )
				return;

			string parameterName;
			if ( this.Started )
				this.sql.Append( "UNION " );
			this.sql.Append( "SELECT " );

			// Adiciona o nome da coluna
			this.AppendColumnName( "clm", out parameterName )
				.IncludeSqlParameter( parameterName, columnName.ColumnNameWithTenant() );

			// Adiciona o valor
			this.sql.Append( ',' );
			this.AppendColumnName( "value", out parameterName )
				.IncludeSqlParameter( parameterName, columnValue );
			this.sql.AppendLine();
		}

		private UpdateCommandBuilder IncludeSqlParameter( string parameterName, string value )
		{
			this.parameters.Add(
				new SqlParameter( parameterName, System.Data.SqlDbType.VarChar, value.Length )
				{
					Direction = System.Data.ParameterDirection.Input,
					Value = value
				} );
			return this;
		}

		private UpdateCommandBuilder IncludeSqlParameter( string parameterName, byte[] value )
		{
			this.parameters.Add(
				new SqlParameter( parameterName, System.Data.SqlDbType.VarBinary, value.Length )
				{
					Direction = System.Data.ParameterDirection.Input,
					Value = value
				} );
			return this;
		}

		private UpdateCommandBuilder AppendColumnName( string columnName, out string parameterName )
		{
			parameterName = String.Concat( "@", this.parameterCount++ );
			sql.Append( parameterName ).Append( ' ' ).Append( columnName );
			return this;
		}

		public string ToSqlString()
		{
			this.sql.Insert( 0, SqlUpdateCommandPart1 );
			this.sql.Append( SqlUpdateCommandPart2 );
			return this.sql.ToString();
		}

		public SqlCommand CreateCommand( SqlConnection connection )
		{
			SqlCommand cmd = connection.CreateCommand();

			cmd.CommandType = System.Data.CommandType.Text;
			cmd.CommandText = this.ToSqlString();
			this.parameters.ForEach( p => cmd.Parameters.Add( p ) );

			return cmd;
		}
	}

}
